﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Permissions;
using System.Windows.Interop;
using System.Security;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;

namespace XWord.WPF.ApplicationFramework.UserInterface
{
    //From Expression
    public class SplashScreen : IDisposable
    {
        // Fields
        private IntPtr bitmapHandle = IntPtr.Zero;
        private int bitmapHeight;
        private int bitmapWidth;
        private static bool isClassRegistered;
        private bool isClosed;
        private UnsafeNativeMethods.WndProc splashWindowProcedureCallback;
        private static string windowClassName = "XWordSplashScreenWindowClass";
        private IntPtr windowHandle = IntPtr.Zero;

        // Methods
        [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
        public SplashScreen(Module module, int resourceId)
        {
            IntPtr hINSTANCE = Marshal.GetHINSTANCE(module);
            
            this.bitmapHandle = UnsafeNativeMethods.LoadBitmap(hINSTANCE, new IntPtr(resourceId));
            this.splashWindowProcedureCallback = new UnsafeNativeMethods.WndProc(this.SplashWindowProcedure);
        }

        public void Close()
        {
            if (!this.isClosed)
            {
                this.isClosed = true;
                UnsafeNativeMethods.PostMessage(new HandleRef(this, this.windowHandle), 0x10, IntPtr.Zero, IntPtr.Zero);
                if (this.bitmapHandle != IntPtr.Zero)
                {
                    UnsafeNativeMethods.DeleteObject(this.bitmapHandle);
                    this.bitmapHandle = IntPtr.Zero;
                }
            }
        }

        public void Close(IntPtr handle)
        {
            if (this.windowHandle != IntPtr.Zero)
            {
                UnsafeNativeMethods.SetForegroundWindow(handle);
            }
            this.Close();
        }

        private void CreateWindow()
        {
            if (!isClassRegistered)
            {
                this.RegisterClass();
            }
            if (isClassRegistered)
            {
                this.ReallyCreateWindow();
                if (this.windowHandle != IntPtr.Zero)
                {
                    UnsafeNativeMethods.ShowWindow(this.windowHandle, 5);
                }
            }
        }

        public void Dispose()
        {
            this.Dispose(true);
        }

        protected virtual void Dispose(bool disposing)
        {
            this.Close(IntPtr.Zero);
            GC.SuppressFinalize(this);
        }

        private void GetBitmapDimensions()
        {
            int cb = Marshal.SizeOf(typeof(UnsafeNativeMethods.BITMAP));
            IntPtr lpvObject = Marshal.AllocCoTaskMem(cb);
            UnsafeNativeMethods.GetObject(this.bitmapHandle, cb, lpvObject);
            UnsafeNativeMethods.BITMAP bitmap = (UnsafeNativeMethods.BITMAP)Marshal.PtrToStructure(lpvObject, typeof(UnsafeNativeMethods.BITMAP));
            this.bitmapWidth = bitmap.bmWidth;
            this.bitmapHeight = bitmap.bmHeight;
            Marshal.FreeCoTaskMem(lpvObject);
        }

        private void OnPaint(IntPtr hdc)
        {
            if (this.bitmapHandle != IntPtr.Zero)
            {
                IntPtr ptr = UnsafeNativeMethods.CreateCompatibleDC(hdc);
                IntPtr hgdiobj = UnsafeNativeMethods.SelectObject(ptr, this.bitmapHandle);
                UnsafeNativeMethods.BitBlt(hdc, 0, 0, this.bitmapWidth, this.bitmapHeight, ptr, 0, 0, 0xcc0020);
                UnsafeNativeMethods.SelectObject(ptr, hgdiobj);
                UnsafeNativeMethods.DeleteDC(ptr);
            }
        }

        private void ReallyCreateWindow()
        {
            int bitmapWidth = this.bitmapWidth;
            int bitmapHeight = this.bitmapHeight;
            int systemMetrics = UnsafeNativeMethods.GetSystemMetrics(0);
            int num4 = UnsafeNativeMethods.GetSystemMetrics(1);
            uint dwStyle = 0x80000000;
            uint dwExStyle = 0x188;
            IntPtr moduleHandle = UnsafeNativeMethods.GetModuleHandle(null);
            IntPtr desktopWindow = UnsafeNativeMethods.GetDesktopWindow();
            this.windowHandle = UnsafeNativeMethods.CreateWindowEx(dwExStyle, windowClassName, "", dwStyle, (systemMetrics - bitmapWidth) / 2, (num4 - bitmapHeight) / 2, bitmapWidth, bitmapHeight, desktopWindow, IntPtr.Zero, moduleHandle, IntPtr.Zero);
        }

        private void RegisterClass()
        {
            IntPtr moduleHandle = UnsafeNativeMethods.GetModuleHandle(null);
            UnsafeNativeMethods.WNDCLASSEX lpwcx = new UnsafeNativeMethods.WNDCLASSEX
            {
                cbSize = (uint)Marshal.SizeOf(typeof(UnsafeNativeMethods.WNDCLASSEX)),
                cbClsExtra = 0,
                cbWndExtra = 0,
                hbrBackground = IntPtr.Zero,
                hCursor = IntPtr.Zero,
                hIcon = IntPtr.Zero,
                hIconSm = IntPtr.Zero,
                hInstance = moduleHandle,
                lpfnWndProc = this.splashWindowProcedureCallback,
                lpszClassName = windowClassName,
                lpszMenuName = null,
                style = 0
            };
            if (UnsafeNativeMethods.RegisterClassExW(ref lpwcx) != 0)
            {
                isClassRegistered = true;
            }
        }

        public void Show()
        {
            if (this.windowHandle == IntPtr.Zero)
            {
                new Thread(new ThreadStart(this.ThreadMethod)) { IsBackground = true }.Start();
            }
        }

        [SuppressUnmanagedCodeSecurity]
        private IntPtr SplashWindowProcedure(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            if (msg == 15)
            {
                UnsafeNativeMethods.PAINTSTRUCT lpPaint = new UnsafeNativeMethods.PAINTSTRUCT();
                IntPtr hdc = UnsafeNativeMethods.BeginPaint(hWnd, out lpPaint);
                this.OnPaint(hdc);
                UnsafeNativeMethods.EndPaint(hWnd, ref lpPaint);
                return IntPtr.Zero;
            }
            return UnsafeNativeMethods.DefWindowProc(hWnd, msg, wParam, lParam);
        }

        [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
        private void ThreadMethod()
        {
            if (this.bitmapHandle != IntPtr.Zero)
            {
                this.GetBitmapDimensions();
                this.CreateWindow();
                MSG msg = new MSG();
                while (UnsafeNativeMethods.GetMessage(ref msg, this.windowHandle, 0, 0) > 0)
                {
                    UnsafeNativeMethods.TranslateMessage(ref msg);
                    UnsafeNativeMethods.DispatchMessage(ref msg);
                }
                this.windowHandle = IntPtr.Zero;
                GC.KeepAlive(this);
            }
        }
    }


}
